package com.mingqijia.gassafety.authority.vcc.facade.impl;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestController;

import com.gap.cache.redis.RedisCache;
import com.mingqijia.gassafety.authority.api.resp.company.CompanyLiteRespDTO;
import com.mingqijia.gassafety.authority.api.resp.manager.UserProfileRespDTO;
import com.mingqijia.gassafety.authority.api.resp.resource.ResourceLiteRespDTO;
import com.mingqijia.gassafety.authority.domain.adapter.BenAdapter;
import com.mingqijia.gassafety.authority.vcc.config.VccOauthConfig;
import com.mingqijia.gassafety.authority.vcc.facade.VccAuthorityManageFacade;
import com.mingqijia.gassafety.authority.vcc.provider.OrgMatchEvent;
import com.mingqijia.gassafety.authority.vcc.valueobject.VccUserVO;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.facade.BaseFacade;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.vcc.VccAuthoritiesProvider;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RestController
@RequiredArgsConstructor
public class VccAuthorityManageFacadeImpl extends BaseFacade implements VccAuthorityManageFacade {

    private final VccOauthConfig oauthConfig;

    private final VccAuthoritiesProvider provider;

    private final BenAdapter benAdapter;

    private final RedisCache cacheManager;

    private final Map<String, List<Object>> functions = new HashMap<>();
    private final RedisCache redisCache;

    @Override
    public void authorize(HttpServletResponse response, HttpServletRequest request) throws IOException {
        response.sendRedirect(oauthConfig.getAuthorizeUrl(request));
    }

    @Override
    public String refreshBenToken() {
        throw new GlobalAuthenticationException("该接口未实现");
    }

    @Override
    public String genCaptcha(String username, HttpServletResponse response) {
        throw new GlobalAuthenticationException("该接口不可用");
    }

    @Override
    public UserProfileRespDTO getUserProfile(HttpServletRequest request) {
        VccUserVO backend = (VccUserVO) getUserBackend();
        JSONObject userDetail = provider.getUserDetail(backend.getToken());

        UserProfileRespDTO profile = new UserProfileRespDTO();
        profile.setUserId(backend.getUserId());
        profile.setName(userDetail.getStr("userName"));
        profile.setUsername(userDetail.getStr("userCode"));
        profile.setSystemOptions(userDetail.getJSONArray("systemOptions"));
        profile.setWorkingSpId(userDetail.getJSONObject("loginSp").getStr("spId"));

        // 本安token
        profile.setBenToken(benAdapter.getBenToken());

        profile.setLogoutUrl(oauthConfig.getLogoutUrl(request));
        JSONObject availableSps = provider.getAvailableSp(backend.getToken());
        if (ObjectUtils.isEmpty(availableSps)) {
            return profile;
        }

        List<CompanyLiteRespDTO> authorizedSps = new ArrayList<>();
        availableSps.getJSONArray("items").forEach(sp -> {
            JSONObject spObj = JSONUtil.parseObj(sp);
            CompanyLiteRespDTO resp = new CompanyLiteRespDTO();
            resp.setOrgType(3);
            resp.setId(spObj.getStr("id"));
            resp.setName(spObj.getStr("spName"));
            resp.setAdCode(spObj.getStr("registeredAddress"));
            resp.setCode(spObj.getStr("stdCode"));
            authorizedSps.add(resp);
        });
        profile.setAuthorizedSp(authorizedSps);

        // 保存组织信息
        ApplicationUtils.CTX.publishEvent(new OrgMatchEvent(this, profile));

        return profile;
    }

    @Override
    public List<ResourceLiteRespDTO> getAuthorizedRes(String workingSpId) throws Exception {
        long current = System.currentTimeMillis();
        VccUserVO backend = (VccUserVO) getUserBackend();
        backend.setWorkingSpId(workingSpId);

        // 切换当前企业
        JSONObject changeSpId = provider.changeSpId(backend.getToken(), workingSpId);
        log.info("用户[{}]切换至企业[{}], duration={}", backend.getName(), workingSpId, System.currentTimeMillis() - current);

        redisCache.deleteObject(Constants.LOGIN_USER_PERMISSION_REDIS_KEY+":"+backend.getUserId());

        functions.clear();
        current = System.currentTimeMillis();
        CompletableFuture<JSONArray> funcTask = CompletableFuture.supplyAsync(() -> {
            // 获取用户按钮权限
            JSONObject privileges = provider.getUserPriv(backend.getToken(), oauthConfig.getAppCode());
            return privileges.getJSONArray("items");
        });

        CompletableFuture<JSONArray> menuTask = CompletableFuture.supplyAsync(() -> {
            // 获取菜单权限
            JSONObject authorities = provider.getPageMenu(backend.getToken(), oauthConfig.getAppCode());
            return authorities.getJSONArray("items");
        });

        // // 获取用户按钮权限
        // JSONObject privileges = provider.getUserPriv(backend.getToken(), oauthConfig.getAppCode());
        // JSONArray privArray = privileges.getJSONArray("items");

        JSONArray privArray = funcTask.get();
        JSONArray resources = menuTask.get();
        log.info("获取菜单和按钮权限[spId={}]：func={}，menu={}, duration={}", workingSpId, resources, privArray,
            System.currentTimeMillis() - current);
        for (Object privilege : privArray) {
            JSONObject privObj = JSONUtil.parseObj(privilege);
            functions.put(privObj.getStr("funcCode"), privObj.getJSONArray("compList"));
        }

        // // 获取菜单权限
        // JSONObject authorities = provider.getPageMenu(backend.getToken(), oauthConfig.getAppCode());
        // JSONArray resources = authorities.getJSONArray("items");

        List<ResourceLiteRespDTO> liteResponses = new ArrayList<>();
        if (ObjectUtils.isEmpty(resources)) {
            log.warn("获取菜单权限失败，菜单权限返回为空。");
            return liteResponses;
        }

        cacheManager.setCacheObject(String.format(Constants.KEY_MANAGE_PROFILE_CODE, backend.getToken()), backend,
            backend.getExpireTime() - 120, TimeUnit.SECONDS);

        current = System.currentTimeMillis();
        for (Object resource : resources) {
            liteResponses.add(buildMenuStruct(JSONUtil.parseObj(resource)));
        }
        log.info("构建菜单树[duration={}]", System.currentTimeMillis() - current);
        return liteResponses;
    }

    private ResourceLiteRespDTO buildMenuStruct(JSONObject menu) {
        ResourceLiteRespDTO resp = new ResourceLiteRespDTO();
        resp.setType(1);
        // resp.setFuncCode(menu.getStr("funcCode"));
        resp.setResId(menu.getStr("id"));
        resp.setName(menu.getStr("menuName"));
        resp.setOrdinal(menu.getInt("seq"));
        resp.setUrl(menu.getStr("menuUrl"));
        resp.setChildren(new ArrayList<>());

        // 获取当前菜单下的按钮
        List<Object> funcs = functions.get(menu.getStr("funcCode"));
        if (!CollectionUtils.isEmpty(funcs)) {
            for (Object func : funcs) {
                ResourceLiteRespDTO funcResp = new ResourceLiteRespDTO();
                funcResp.setType(2);
                funcResp.setFuncCode(func.toString());
                resp.getChildren().add(funcResp);
            }
        }

        // 获取子菜单
        JSONArray childrenMenu = menu.getJSONArray("children");
        if (CollectionUtils.isEmpty(childrenMenu)) {
            return resp;
        }
        for (Object childMenu : childrenMenu) {
            resp.getChildren().add(buildMenuStruct(JSONUtil.parseObj(childMenu)));
        }
        return resp;
    }
}
